// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.eva.android.shortvideo;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.VideoView;

import com.x52im.rbchat.R;

import java.io.File;

/**
 * 视频播放器封装类。
 *
 * @author Jack Jiang
 * @since 4.4
 */
public class VideoPlayWrapper
{
    private final static String TAG = VideoPlayWrapper.class.getSimpleName();

    /** 父activity */
    private Activity parentActivity = null;
    /** 要播放的视频绝对路径，本参数目前由首次调用 {@link #play(String)} 方法时传入 */
    private String videoFilePath = null;

    // 播放器实例
    private MediaPlayer mediaPlayer;
    // 视频播放的目标SurfaceView
    private SurfaceView sv;
    // 视频播放的进度
    private ProgressBar seekBar;
    // 视频播放的进度时间显示组件
    private TextView viewCurrentPlayedTime;
    // 视频的总时间
    private TextView viewVideoDuration;
    // 视频暂傍时显示的"继续播放"图标
    private ImageView viewContinuePlayIcon;

    /** 用于刷新的播放进度UI显示：handler对象 */
    private Handler progressRefreshHandler = null;
    /** 用于刷新的播放进度UI显示：Runnable实现内容 */
    private Runnable progressRefreshRunnable = null;
    /** 用于刷新的播放进度UI显示：本次刷新任务是否还未完成 */
    private boolean progressRefreshExcuting = false;

    /** 当前播放状态 */
    private PlayStatus playStatus = PlayStatus.NONE;

    // SurfaceView的回调实现对象
    private Callback callback = new Callback() {
        // SurfaceHolder被修改的时候回调
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            Log.i(TAG, "【视频播放器】[callback]SurfaceHolder 被销毁【3】!");

            // 将正在进行中的播放挂起
//          suspendPlay();

            // 释放MediaPlayer资源
            stop();
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            Log.i(TAG, "【视频播放器】[callback]SurfaceHolder 被创建【1】.");
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
            Log.i(TAG, "【视频播放器】[callback]SurfaceHolder 大小被改变【2】.");
        }
    };

    public VideoPlayWrapper(Activity parentActivity)//, String videoFilePath)
    {
        this.parentActivity = parentActivity;
//      this.videoFilePath = videoFilePath;

        initViews();
        initListeners();
    }

    private void initViews()
    {
        //实例化MediaPlayer对象
        mediaPlayer = new MediaPlayer();

        seekBar = (ProgressBar) parentActivity.findViewById(R.id.common_short_video_player_ac_progressView);
        sv = (SurfaceView) parentActivity.findViewById(R.id.common_short_video_player_ac_videoView);
//      viewVideoNum = (TextView)parentActivity.findViewById(R.id.login_activity_videoNumView);
        viewCurrentPlayedTime = (TextView) parentActivity.findViewById(R.id.common_short_video_player_ac_currentPlayTimeView);
        viewVideoDuration = (TextView) parentActivity.findViewById(R.id.common_short_video_player_ac_videoDurationView);
        viewContinuePlayIcon = (ImageView) parentActivity.findViewById(R.id.common_short_video_player_ac_continuePlayImageView);

        // 为SurfaceHolder添加回调
        sv.getHolder().addCallback(callback);

        // 设置播放时打开屏幕
        sv.getHolder().setKeepScreenOn(true);

        // 4.0版本之下需要设置的属性
        // 设置Surface不维护自己的缓冲区，而是等待屏幕的渲染引擎将内容推送到界面
        // sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        // 借助这个监听器，确保能自动适配视频的显示大小，否则在不同分辨率的手机上，不同尺寸的视频显示时会变形
        mediaPlayer.setOnVideoSizeChangedListener(createOnVideoSizeChangedListener());

//		// 为进度条添加进度更改事件
//		seekBar.setOnSeekBarChangeListener(change);

        progressRefreshHandler = new Handler();
        progressRefreshRunnable = () -> {
            if(mediaPlayer != null && mediaPlayer.isPlaying()) {
                // 当前已播放时长
                final int current = mediaPlayer.getCurrentPosition();

                // 刷新ui显示
                viewCurrentPlayedTime.setText(com.eva.android.ToolKits.durationFormatToString(current / 1000));
                seekBar.setProgress(current);

                // 极端情况下本次循环内可能执行时间超过了时间间隔，此处是防止在前一
                // 次还没有运行完的情况下又重复过劲行，从而出现无法预知的错误
                if (!progressRefreshExcuting) {
                    progressRefreshExcuting = false;

                    // 开始下一个心跳循环
                    progressRefreshHandler.postDelayed(progressRefreshRunnable, 500);
                }
            }
        };
    }

    private void initListeners()
    {
        View.OnClickListener lis = view -> {
            Log.d(TAG, "【视频播放器】正在点击surfaceView，当前playStatus="+playStatus);
            switch (playStatus) {
                case NONE: {
                    if (videoFilePath != null)
                        play(videoFilePath);
                    else
                        Log.d(TAG, "【视频播放器】正在点击surfaceView，且playStatus=NONE，但videoFilePath==null，无法播放视频！");
                }
                break;
                case PAUSED: {
                    resume();
                    break;
                }
                case PLAYING: {
                    pause();
                    break;
                }
            }
        };

        // 点击视频播放区的事件处理（点击就暂停或继续播放）
        sv.setOnClickListener(lis);
        // 点击继续播放按钮的事件处理（点击就继续播放）
        viewContinuePlayIcon.setOnClickListener(lis);
    }

    /**
     * 创建MediaPlayer的视频大小监听器，确保能自动适配视频的显示大小，否则在不同分辨率的手机上，不同尺寸的视频显示时会变形。
     * 将来，可考虑直接使用VideoView控件，它会自动进行适配，无需程序员自行处理。
     *
     * 参考资料：
     * https://blog.csdn.net/u013274497/article/details/79041912
     * https://blog.csdn.net/qq137464739/article/details/84547944
     * https://blog.csdn.net/weixin_61845324/article/details/124098076
     *
     * @since 8.3
     */
    private MediaPlayer.OnVideoSizeChangedListener createOnVideoSizeChangedListener(){
        MediaPlayer.OnVideoSizeChangedListener lis = new MediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
                changeVideoSize();
            }

            // 计算并适配视频的显示大小
            public void changeVideoSize() {
                try{
                    int videoWidth = mediaPlayer.getVideoWidth();
                    int videoHeight = mediaPlayer.getVideoHeight();

                    int surfaceWidth = sv.getWidth();
                    int surfaceHeight = sv.getHeight();

                    // 根据视频尺寸去计算->视频可以在sufaceView中放大的最大倍数。
                    float max;
                    if (parentActivity.getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
                        // 竖屏模式下按视频宽度计算放大倍数值
                        max = Math.max((float) videoWidth / (float) surfaceWidth, (float) videoHeight / (float) surfaceHeight);
                    } else {
                        // 横屏模式下按视频高度计算放大倍数值
                        max = Math.max(((float) videoWidth / (float) surfaceHeight), (float) videoHeight / (float) surfaceWidth);
                    }

                    // 视频宽高分别/最大倍数值 计算出放大后的视频尺寸
                    videoWidth = (int) Math.ceil((float) videoWidth / max);
                    videoHeight = (int) Math.ceil((float) videoHeight / max);

                    // 无法直接设置视频尺寸，将计算出的视频尺寸设置到surfaceView 让视频自动填充。
    //              RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(videoWidth, videoHeight);
    //              params.addRule(RelativeLayout.CENTER_VERTICAL, mParent.getId());
    //              mSurfaceView.setLayoutParams(params);

                    Log.d(TAG,"【播放短视频时自动计算视频显示大小】 [videoWidth="+videoWidth+", videoHeight="+videoHeight
                            +"], [surfaceWidth="+surfaceWidth+", surfaceHeight="+surfaceHeight+"], max="+max
                            +", 计算结果=> [videoWidth="+videoWidth+", videoHeight="+videoHeight+"]");

                    // 在UI上适配视频显示大小
                    ViewGroup.LayoutParams params = sv.getLayoutParams();
                    params.width = videoWidth;
                    params.height = videoHeight;
                    sv.setLayoutParams(params);
                } catch (Exception e){
                    Log.w(TAG, "【播放短视频时自动计算视频显示大小】发生了异常："+e.getMessage(), e);
                }
            }
        };
        return lis;
    }

    /**
     * 根据当前播放状态，刷新视频播放区中心的播放状态图标显示。
     */
    private void regreshUI()
    {
        switch (playStatus) {
            case NONE:
            case PAUSED:
                viewContinuePlayIcon.setVisibility(View.VISIBLE);
                break;
            case PLAYING:
                viewContinuePlayIcon.setVisibility(View.GONE);
                break;
        }
    }

    /**
     * 停止播放。
     * <p>
     * 本方法一般用于SurfaceHold被销毁的场景下，比如按home键时，Callback中的SurfaceHold销毁回调中调用。
     * 如果不是上面的情况，只需要调用mediaPlayer.stop()、再次播放时时只需mediaPlayer.reset()就可以了。
     */
    public void stop()
    {
        stop_progressRefresh();

        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
////		btn_play.setEnabled(true);
//			isPlaying = false;
        }

        if(mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }

        this.playStatus = PlayStatus.NONE;
        this.regreshUI();
    }

    /**
     * 暂停播放（本方法在视频播放中时可调用）。
     */
    public void pause()
    {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            this.playStatus = PlayStatus.PAUSED;
            this.stop_progressRefresh();
            this.regreshUI();
            Log.d(TAG, "【视频播放器】播放已被暂停。");
        }
    }

    /**
     * 恢复播放（本方法在播放完成或播放暂停后可调用）。
     */
    public void resume()
    {
        if(playStatus == PlayStatus.PAUSED) {
            mediaPlayer.start();
            this.playStatus = PlayStatus.PLAYING;
            this.start_progressRefresh();
            Log.d(TAG, "【视频播放器】播放已被恢复。。");
        } else {
            this.play(this.videoFilePath);
            Log.d(TAG, "【视频播放器】播放已重新开始。。。");
        }

        this.regreshUI();
    }

    /**
     * 开始或重新播放。
     */
    public void play(String videoFilePath)
    {
        this.videoFilePath = videoFilePath;
        try {
            final File file = new File(this.videoFilePath);

            if(file == null) {
                Log.w(TAG, "没有读取到视频文件"+this.videoFilePath+"的信息！");
                throw new Exception("【视频播放器】[play]没有读取到视频文件信息，本次播放不能继续！(file=null)");
            }

            if (!file.exists()) {
                Log.w(TAG, "【视频播放器】视频文件"+this.videoFilePath+"不存在!");
                throw new Exception("【视频播放器】[play]视频文件不存在，本次播放不能继续！(file="+file.getAbsolutePath()+")");
            }

            // 当SurfaceHold被销毁的场景下，比如按home键时，mediaPlay必须要被release，再次使用时需要重新new一个
            // mediaPlay才行，否则如果只是简单的stop，则再次播放时尝试reset会报“IllegalStateException”错误而无法播放
            if(mediaPlayer == null) {
                Log.w(TAG, "【视频播放器】[play]mediaPlayer居然=null，它不是在initViews里就实化好了吗？");
                mediaPlayer = new MediaPlayer();
            }
            // 在SurfaceHold没被销毁的场景下，停止播放时只需要调用mediaPlayer.stop()、再次播放时时只需mediaPlayer.reset()就可以了
            else
                mediaPlayer.reset();

            // 重置进度条显示
            seekBar.setProgress(0);

            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            // 设置播放的视频源
            mediaPlayer.setDataSource(file.getAbsolutePath());
//			// 设置显示视频的SurfaceHolder
//			mediaPlayer.setDisplay(sv.getHolder());
            Log.i(TAG, "【视频播放器】[play]mediaPlayer开始装载视频文件\""+file.getAbsolutePath()+"\"");
            // 缓冲数据
            mediaPlayer.prepareAsync();
            mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    try {
                        Log.i(TAG, "【视频播放器】[play_onPrepared]mediaPlayer装载视频文件\""+file.getAbsolutePath()+"\" 完成.");

                        // 设置显示视频的SurfaceHolder
                        mediaPlayer.setDisplay(sv.getHolder());

                        // 取出可能的上次播放时间位置
                        int msec = 0;//MyApplication.getInstance().getVideosHolder().getCurrentPosition();

                        Log.i(TAG, "【视频播放器】[play_onPrepared]mediaPlayer装载视频文件\""+file.getAbsolutePath() +"\" 马上开始播放(起始位置"+msec+"ms)...");

                        // 开始播放
                        mediaPlayer.start();
                        // 保存播放状态
                        playStatus = PlayStatus.PLAYING;

                        // 刷新ui
                        regreshUI();

                        // 按照初始位置播放
                        mediaPlayer.seekTo(msec);
//                        // 复位上次保存的位置
//                        MyApplication.getInstance().getVideosHolder().setCurrentPosition(0);
                        // 设置进度条的最大进度为视频流的最大播放时长
                        seekBar.setMax(mediaPlayer.getDuration());

                        Log.i(TAG, "【视频播放器】>>>>>>>>>>> mediaPlayer.getDuration()="+mediaPlayer.getDuration());

                        //## Bug FIX 20160725 START
                        // 经测，在孔提供的某款平板上有一定几率getDuration=0，此时视频播放会卡在这！
                        // 出现这种情况时，就想办法让它重新播放吧，不然咋整？？？！！！
                        if(mediaPlayer.getDuration() <= 0) {
                            Log.e(TAG, "【视频播放器】[play_onPrepared]本次竟然mediaPlayer.getDuration()="
                                    +mediaPlayer.getDuration()+"！赶紧调用delayToPlayForErrorEtc()，不然视频就不往下播放了！");

                            // 发生错误重新播放
                            playComplete(true);
                            return;
                        }
                        //## Bug FIX 20160725 END

                        // 显示视频时间
                        viewVideoDuration.setText(com.eva.android.ToolKits.durationFormatToString(mediaPlayer.getDuration()/1000));

                        // 开始线程，更新进度条的刻度
                        start_progressRefresh();
                    } catch (Exception e) {
                        Log.e(TAG, "【视频播放器】[play_onPrepared]出错了，", e);

                        // 发生错误重新播放
                        playComplete(true);
                    }
                }
            });
            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    try {
                        // 在播放完毕被回调
//					    btn_play.setEnabled(true);
                        Log.i(TAG, "【视频播放器】[play_onCompletion]视频文件\""+file.getAbsolutePath()+"\" 播放完毕.");

                        // 重置mediaplayer等待下一次播放
                        if(mediaPlayer != null)
                        {
//							mediaPlayer.release();
                            // 此种正常循环播放时只需要mediaplay.stop()即可，不需要像SurfaceHolder销毁时一样必须要release!
                            mediaPlayer.stop();
                        }

                        // 视频正常播放完成时的通知
                        playComplete(false);
                    } catch (Exception e) {
                        Log.e(TAG, "【视频播放器】[play_onCompletion]出错了，", e);

                        // 发生错误重新播放
                        playComplete(true);
                    }
                }
            });

            // 视频播放出错事件监听器
            mediaPlayer.setOnErrorListener(new OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    Log.e(TAG, "【视频播放器】[play_onError]onError中的回调：播放出错，马上自动重播，what="+what+",extra="+extra);
                    // 发生错误重新播放
                    playComplete(true);
//					isPlaying = false;
                    return false;
                }
            });
        } catch (Exception e) {
            Log.e(TAG, "【视频播放器】[play]播放视频广告时发生了未知错误，"+e.getMessage(), e);

            // 发生错误重新播放
            playComplete(true);
        }
    }

    /**
     * 本方法将在视频正常播放完成、或者播放时出现任何错误地情况下都会确保被调用。
     * <p>
     *     视频播放结束（包括正常或不正常地线束）时要做的事，都可以放到本方法。
     * </p>
     *
     * @param withError true表示播放出错时的结束，否则表示正常播放结束
     */
    protected void playComplete(boolean withError)
    {
        Log.d(TAG, "播放完成，到这里来了吗？？？withError="+withError);
        this.playStatus = PlayStatus.NONE;
        this.stop_progressRefresh();
        this.regreshUI();

        if(!withError)
            // 播放完成后将进度条显示一拉到底（不然因上面的更新间隔而永远到不了100%处哦）
            seekBar.setProgress(seekBar.getMax());
    }

    /**
     * 停止播放进度刷新线程。
     */
    public void stop_progressRefresh()
    {
        progressRefreshHandler.removeCallbacks(progressRefreshRunnable);
        progressRefreshExcuting = false;
    }

    /**
     * 开启播放进度刷新线程。
     */
    public void start_progressRefresh()
    {
        stop_progressRefresh();
        progressRefreshHandler.postDelayed(progressRefreshRunnable, 0);
    }

    /**
     * 播放状态。
     */
    enum PlayStatus {
        NONE,
        PLAYING,
        PAUSED
    }
}
